package cn.ibdsr.human.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.AbstractHttpMessage;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author chenglx
 * @version V1.0
 * @description HTTP客户端工具类
 * @createDate 2020/3/23 11:33
 * <p>
 * Date Author Description
 * ------------------------------------------------------
 * 2020/3/23 chenglx init
 */

@Slf4j
public class HttpClientUtils {

    private static CloseableHttpClient httpClient;
    private static CloseableHttpClient httpsClient;
    public static PoolingHttpClientConnectionManager connManager;

    private static final String DEFAULT_SCHEME = "http";

    static {
//        try {
//            // 信任管理器
//            X509TrustManager tm = new X509TrustManager() {
//                @Override
//                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//                }
//
//                @Override
//                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//                }
//
//                @Override
//                public X509Certificate[] getAcceptedIssuers() {
//                    return null;
//                }
//            };
//
//            SSLContext sslContext = SSLContext.getInstance("TLS");
//            // 初始化SSL上下文
//            sslContext.init(null, new TrustManager[]{tm}, null);
//            // SSL套接字连接工厂，NoopHostnameVerifier为信任所有服务器
//            SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
//            // 通过setSSLSocketFactory(factory)保证httpclient实例能发送Https请求
//            httpsClient = HttpClients.custom().setSSLSocketFactory(factory).setMaxConnTotal(50).setMaxConnPerRoute(50)
//                    .setDefaultRequestConfig(RequestConfig.custom().setConnectionRequestTimeout(60000)
//                            .setConnectTimeout(60000).setSocketTimeout(60000).build()).build();
//        } catch (Exception e) {
//            log.error("静态初始化模块发生异常", e);
//        }

        // 连接池管理
        connManager = new PoolingHttpClientConnectionManager();
        // Validate connections after 20 seconds of inactivity
        connManager.setValidateAfterInactivity(20000);
        // Configure total max or per route limits for persistent connections
        // that can be kept in the pool or leased by the connection manager.
        connManager.setMaxTotal(200);
        connManager.setDefaultMaxPerRoute(100);

        // keepAlive策略，复用连接。默认采用header声明的策略，如果没有，默认设置为20s
        ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
//                HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
//                while (it.hasNext()) {
//                    HeaderElement he = it.nextElement();
//                    String name = he.getName();
//                    String value = he.getValue();
//                    if (value != null && name.equalsIgnoreCase("timeout")) {
//                        return Long.parseLong(value) * 1000;
//                    }
//                }
                return 10 * 1000;
            }
        };

        // Create global request configuration
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                // 从连接池获取连接的超时时间（毫秒）
                .setConnectionRequestTimeout(500)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000)
                .build();

        HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                if (executionCount >= 3) {// 如果已经重试了3次，就放弃
                    return false;
                }
                if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接，那么就重试
                    return true;
                }
                if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
                    return false;
                }
                if (exception instanceof InterruptedIOException) {// 超时
                    return true;
                }
                if (exception instanceof UnknownHostException) {// 目标服务器不可达
                    return false;
                }
                if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
                    return false;
                }
                if (exception instanceof SSLException) {// ssl握手异常
                    return false;
                }
                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();
                // 如果请求是幂等的，就再次尝试
                if (!(request instanceof HttpEntityEnclosingRequest)) {
                    return true;
                }
                return false;
            }
        };

        // 创建HTTP客户端
        httpClient = HttpClients.custom()
                .setConnectionManager(connManager)
                .setKeepAliveStrategy(keepAliveStrategy)
                .build();

        // 创建HTTPS客户端
        httpsClient = HttpClients.custom()
                .setDefaultRequestConfig(defaultRequestConfig)
//                .setRetryHandler(retryHandler)
//                .setConnectionTimeToLive(20, TimeUnit.SECONDS)
                .setConnectionManager(connManager)
                .setKeepAliveStrategy(keepAliveStrategy)
                .setSSLSocketFactory(createSSLConnSocketFactory())
                .build();


//        MonitorTask monitorTask = new MonitorTask();
//        monitorTask.start();
    }


    static class MonitorTask extends Thread {
        @Override
        public void run(){
            try {
                while(true){
                    Thread.sleep(1000);
                    log.debug("------------closeExpiredConnections--------------");
                    //Close expired connections
                    connManager.closeExpiredConnections();
                }
            } catch (Exception e){
                //terminate
            }
        }
    }



    /**
     * 通过httpClient获取http请求结果，使用get方法
     *
     * @param host   请求服务地址
     * @param port   端口号
     * @param path   文件路径
     * @param params 请求参数
     * @return http请求结果字符串
     * @throws URISyntaxException
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static String obtainHttpGetResponse(String host, Integer port, String path, Map<String, String> params)
            throws URISyntaxException, ClientProtocolException, IOException {
        HttpGet httpGet = createHttpGet("http", host, port, path, params, null);
        // httpGet.setHeader("Connection", "close");
        CloseableHttpResponse response = httpClient.execute(httpGet);
        return getResponseEntityString(response, httpGet);
    }

    /**
     * 发起HTTPS的GET请求
     * @param host
     * @param port
     * @param path
     * @param params
     * @return
     * @throws URISyntaxException
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static String obtainHttpsGetResponse(String host, Integer port, String path, Map<String, String> params)
            throws URISyntaxException, ClientProtocolException, IOException {
        HttpGet httpGet = createHttpGet("https", host, port, path, params, null);
        // httpGet.setHeader("Connection", "close");
        CloseableHttpResponse response = httpsClient.execute(httpGet);
        return getResponseEntityString(response, httpGet);
    }


    public static byte[] obtainGetRequestBytes(String uri) throws IOException {
        HttpGet httpGet = new HttpGet(uri);
        CloseableHttpResponse response =  httpsClient.execute(httpGet);
        return getResponseEntityBytes(response, httpGet);
    }


    /**
     * 发起HTTPS的POST请求
     * @param host
     * @param port
     * @param path
     * @param queryParams 请求行中的参数
     * @param bodyParams 请求主体中的参数
     * @return
     * @throws URISyntaxException
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static HttpResponse obtainHttpsPostResponse(String host, Integer port, String path, Map<String, String> queryParams,
                                                 Map<String, String> bodyParams)
            throws URISyntaxException, ClientProtocolException, IOException {
        return obtainHttpsPostResponse(host, port, path, queryParams, bodyParams, ContentType.APPLICATION_JSON);
    }

    public static <T> HttpResponse obtainHttpsPostResponse(String host, Integer port, String path, Map<String, String> queryParams,
                                                       Object bodyParams, ContentType requestContentType)
            throws URISyntaxException, ClientProtocolException, IOException {
        // MULTIPART/FORM_DATA 请求的处理
//        if (ContentType.MULTIPART_FORM_DATA.getMimeType().equals(requestContentType.getMimeType())) {
//            return multipartFormPost(host, port, path, queryParams, (Map<String, byte[]>) bodyParams);
//        }
        // 其他请求的处理
        HttpPost request = createHttpPost("https", host, port, path, queryParams, null, bodyParams);
        // request.setHeader("Connection", "close");
        printHeaders(request.getAllHeaders(), "request headers");
        HttpResponse response = httpsClient.execute(request);
        printHeaders(response.getAllHeaders(), "response headers");
        return response;
//        return httpsClient.execute(request);
    }

    private static void printHeaders(Header[] headers, String mark) {
        log.info("=========={}===========", mark);
        for (int i = 0; i < headers.length; ++i) {
            Header h = headers[i];
            log.info("==========Header name={}; value={}", h.getName(), h.getValue());
        }

    }

    public static String obtainHttpsDeleteResponse(String host, Integer port, String path, Map<String, String> params)
            throws URISyntaxException, ClientProtocolException, IOException {
        HttpDelete httpDelete = createHttpDelete("https", host, port, path, params, null);
        // httpDelete.setHeader("Connection", "close");
        CloseableHttpResponse response = httpsClient.execute(httpDelete);
        return getResponseEntityString(response, httpDelete);
    }


    public static HttpResponse multipartFormPost(String host, Integer port, String path, Map<String, String> queryParams,
                                         Map<String, byte[]> bodyParams)
            throws URISyntaxException, ClientProtocolException, IOException {
        HttpPost request = createHttpPost("https", host, port, path, queryParams, null, null);
        // 增加multipart数据
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        if (!CollectionUtils.isEmpty(bodyParams)) {
            for (Map.Entry<String, byte[]> entry : bodyParams.entrySet()) {
                builder.addBinaryBody(entry.getKey(), entry.getValue(), ContentType.IMAGE_JPEG, entry.getValue().hashCode() + ".jpg");
            }
        }
        request.setEntity(builder.build());
        HttpEntity reqEntity = request.getEntity();
        log.debug("multipartFormPost(): contentType={}; contentLength={}; isStreaming={}", reqEntity.getContentType(), reqEntity.getContentLength(), reqEntity.isStreaming());
//        BufferedReader reader = new BufferedReader(new InputStreamReader(reqEntity.getContent()));
//        String line;
//        while ((line = reader.readLine()) != null) {
//            log.debug("multipartFormPost(): body: {}", line);
//        }
        log.debug("multipartFormPost(): executing request " + request.getRequestLine());
        return httpClient.execute(request);
    }

    private static HttpGet createHttpGet(String scheme, String host, Integer port, String path, Map<String, String> params,
                                         Map<String, String> headers) throws URISyntaxException {
        HttpGet httpGet = new HttpGet(createUri(scheme, host, port, path, params));
        setHeaders(httpGet, headers);
        return httpGet;
    }


    /**
     * 发起HTTP POST请求。
     * 请求实体的 "Content-Type" 为 "application/json"
     * @param scheme
     * @param host
     * @param port
     * @param path
     * @param queryParams HTTP请求行中的参数
     * @param headers
     * @param bodyParams HTTP请求body中的参数
     * @return
     * @throws URISyntaxException
     */
    private static HttpPost createHttpPost(String scheme, String host, Integer port, String path, Map<String, String> queryParams,
                                           Map<String, String> headers, Object bodyParams) throws URISyntaxException {
        HttpPost httpPost = new HttpPost(createUri(scheme, host, port, path, queryParams));
        setHeaders(httpPost, headers);
//        if (!CollectionUtils.isEmpty(bodyParams)) {
            // 创建HTTP请求的实体
            StringEntity entity = new StringEntity(JSON.toJSONString(bodyParams), ContentType.APPLICATION_JSON);
            // 设置HTTP请求的实体
            httpPost.setEntity(entity);

//        }
        return httpPost;
    }

    private static HttpDelete createHttpDelete(String scheme, String host, Integer port, String path, Map<String, String> params,
                                               Map<String, String> headers) throws URISyntaxException {
        HttpDelete httpDelete = new HttpDelete(createUri(scheme, host, port, path, params));
        setHeaders(httpDelete, headers);
        return httpDelete;
    }

    private static void setHeaders(AbstractHttpMessage message, Map<String, String> headers) {
        // message.setHeader("Connection", "close");
//        if (!CollectionUtils.isEmpty(headers)) {
//            for (String name : headers.keySet()) {
//                message.addHeader(name, headers.get(name));
//            }
//        }
        message.addHeader("appKey","Xr-DYKjdAFqAwf-lcvXcaVpG5WUMkKPwqPRorgA-3aQ");
    }

    private static URI createUri(String scheme, String host, Integer port, String path, Map<String, String> params)
            throws URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder();

        if (!StringUtils.isEmpty(scheme)) {
            uriBuilder.setScheme(scheme);
        } else {
            uriBuilder.setScheme(DEFAULT_SCHEME);
        }

        uriBuilder.setHost(host);

        if (!StringUtils.isEmpty(port)) {
            uriBuilder.setPort(port);
        }
        if (!StringUtils.isEmpty(path)) {
            uriBuilder.setPath(path);
        }
        if (!CollectionUtils.isEmpty(params)) {
            for (String key : params.keySet()) {
                uriBuilder.setParameter(key, params.get(key));
            }
        }
        return uriBuilder.build();
    }

    /**
     * 处理HTTP响应结果，返回响应的 body
     * @param response
     * @param request 用于打印错误日志
     * @return
     * @throws ParseException
     * @throws IOException
     */
    private static String getResponseEntityString(CloseableHttpResponse response, HttpRequestBase request) throws ParseException, IOException {
        try {
            // 检查http请求状态
            StatusLine statusLine = response.getStatusLine();
            if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
                log.error("processHttpResponse(): 请求失败：uri:{}, statusCode={}, reasonPhrase={}", request.getURI().toString(),
                        statusLine.getStatusCode(), statusLine.getReasonPhrase());
                throw new RuntimeException("请求失败！statusCode=" + statusLine.getStatusCode());
                // return null;
            }

            // 提取http返回数据
            HttpEntity httpEntity = response.getEntity();
            String entityContent = EntityUtils.toString(httpEntity);
            // 关闭HttpEntity的流
            EntityUtils.consume(httpEntity);

            return entityContent;
        } finally {
            response.close();
        }
    }


    /**
     * 处理HTTP响应结果，返回响应的 body
     * @param response
     * @param request 用于打印错误日志
     * @return
     * @throws ParseException
     * @throws IOException
     */
    private static byte[] getResponseEntityBytes(CloseableHttpResponse response, HttpRequestBase request) throws ParseException, IOException {
        try {
            // 检查http请求状态
            StatusLine statusLine = response.getStatusLine();
            if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
                log.error("processHttpResponse(): 请求失败：uri:{}, statusCode={}, reasonPhrase={}", request.getURI().toString(),
                        statusLine.getStatusCode(), statusLine.getReasonPhrase());
                throw new RuntimeException("请求失败！statusCode=" + statusLine.getStatusCode());
                // return null;
            }

            // 提取http返回数据
            HttpEntity httpEntity = response.getEntity();
            Header header = response.getFirstHeader("Content-Type");
            byte[] entityContent = EntityUtils.toByteArray(httpEntity);
            log.debug("getResponseEntityBytes(): Content-Type: {}; entityContent byte length={}", header.toString(), entityContent.length);
            // 关闭HttpEntity的流
            EntityUtils.consume(httpEntity);

            return entityContent;
        } finally {
            response.close();
        }
    }


    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(java.security.cert.X509Certificate[] chain, String authType)
                        throws java.security.cert.CertificateException {
                    return true;
                }
            }).build();
            sslsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        } catch (GeneralSecurityException e) {
            log.error("createSSLConnSocketFactory(): 发生异常: {}", e);
        }
        return sslsf;
    }

    public static String httpGet(String url) {
        return null;
    }


    public static String httpPost(String url) {
        return null;
    }



    public static void main(String[] args) throws IOException, URISyntaxException {
//        String host = "admin-division.ibdsr.cn";
//        Integer port = 80;
//        String path = "/divisionInfoService/getDivisionById";
//        Map<String, String> params = new HashMap<>();
//        params.put("divisionId", "110101001008");
//        String content = HttpClientUtils.obtainHttpGetResponse(host, port, path, params);
//        System.out.println("content: " + content);

//        String host = "webapi.amap.com";
//        Integer port = 80;
//        String path = "/maps/cookie";
//        Map<String, String> params = new HashMap<>();
//        params.put("key", "amap_ver");
//        params.put("value", "1559744451022");
//        String content = HttpClientUtils.obtainHttpGetResponse(host, port, path, params);
//        System.out.println("content: " + content);


//        String host = "api.weixin.qq.com";
//        Integer port = 443;
//        String path = "/sns/jscode2session";
//        Map<String, String> params = new HashMap<>(4);
////        params.put("appid", WechatAppConfig.APP_ID);
////        params.put("secret", WechatAppConfig.APP_SECRET);
//        params.put("js_code", "023y0iB42FabCR01IuE42ZqiB42y0iB6");
//        params.put("grant_type", "authorization_code");
//        String content = HttpClientUtils.obtainHttpsGetResponse(host, port, path, params);
//        System.out.println("content: " + content);


        Map<String, String> map = new HashMap<>(8);
        map.put("a", "1");
        map.put("b", "2");
        map.put("c", "3");

        List<String> list = new ArrayList<>(8);
        list.add("a");
        list.add("b");
        list.add("c");
        System.out.println(JSONArray.toJSON(map));
        System.out.println(JSONArray.toJSON(list));
        System.out.println(JSONArray.toJSON(null));

    }

}
